import random
from math import log

class proteins:
    def __init__(self, seq1, seq2, length1, length2):
        if seq1 != "":
            self.prot1 = seq1
        if seq2 != "":
            self.prot2 = seq2
            
        self.sij = self.matrixofweight()
            
        self.alphabet = "ARNDCQEGHILKMFPSTWYVBZX*"
        
        if length1 != 0 and seq1 == "":
            for i in xrange(length1):
                random.seed()
                k = random.randint(0,19)
                seq1 += self.alphabet[k]
            self.prot1 = seq1
            
        if length2 != 0 and seq2 == "":
            for i in xrange(length2):
                random.seed()
                k = random.randint(0,19)
                seq2 += self.alphabet[k]
            self.prot2 = seq2

    def matrixofweight(self):
        weight = []
        f  = open('EBLOSUM62',  'r')
        InputData = f.read().split()

        def num(s):
            try:
                return int(s)
            except ValueError:
                return "no"

        for i in range(len(InputData)):
            tmp = num(InputData[i])
            if tmp != "no" and tmp != 62:
                weight.append(tmp)
        return weight

    def weightofs(self,a,b):
        aminoacids = self.alphabet
        i = aminoacids.index(a)
        j = aminoacids.index(b)
        return self.sij[i*24 + j]

    def prot_alignment(self):
        n, m = self.lengths()
        s = self.prot1
        t = self.prot2
        D = [[0 for i in xrange(n)] for j in xrange(m)]
        gap_score = -8
        for j in range(n):
            D[0][j] = gap_score*j
        for i in range(m):
            D[i][0] = gap_score*i
    
        for i in range(1,m):
            for j in range(1,n):
                match = D[i-1][j-1] + self.weightofs(s[j-1],t[i-1])
                gaps = D[i][j-1] + gap_score
                gapt = D[i-1][j] + gap_score
                D[i][j] = max(match,gaps,gapt)

        i = m-1
        j = n-1

        t_opt = ''
        s_opt = ''

        while i > 0 and j > 0:
            if D[i][j] - self.weightofs(s[j-1],t[i-1]) == D[i-1][j-1]:
                t_opt = t[i-1] + t_opt  
                s_opt = s[j-1] + s_opt
                i -= 1
                j -= 1
            elif D[i][j] - gap_score == D[i][j-1]:
                s_opt = s[j-1] + s_opt
                t_opt = "_" + t_opt
                j -= 1
            elif D[i][j] - gap_score == D[i-1][j]:
                s_opt = "_" + s_opt
                t_opt = t[i-1] + t_opt
                i -= 1
            else: print "ERROR"

        if j > 0:
            while j > 0:
                s_opt = s[j-1] + s_opt
                t_opt = "_" + t_opt 
                j -= 1
        elif i > 0:
            while i > 0:
                s_opt = "_" + s_opt
                t_opt = t[i-1] + t_opt 
                i -= 1
        return t_opt, s_opt

    def prot_output(self):
        t_opt, s_opt = self.prot_alignment()
        print t_opt
        print s_opt

    def lengths(self):
        return len(self.prot1), len(self.prot2)

    

class dna:
    def __init__(self, seq1, seq2, length1, length2):
        self.dna1 = seq1
        self.dna2 = seq2
        alphabet = "ACGT"
        
        if length1 != 0 and seq1 == "":
            for i in xrange(length1):
                random.seed()
                k = random.randint(0,3)
                seq1 += alphabet[k]
            self.dna1 = seq1
            
        if length2 != 0 and seq2 == "":
            for i in xrange(length2):
                random.seed()
                k = random.randint(0,3)
                seq2 += alphabet[k]
            self.dna2 = seq2


    def weightofs(self,a,b):
        a = a.upper()
        b = b.upper()
        if a == b:
            return 3
        if a == "A" or a == "T":
            if b == "A" or b == "T":
                return 2
            else:
                return 1
        if a == "C" or a == "G":
            if b == "C" or b == "G":
                return 2
            else:
                return 1
        return 

    def dna_alignment(self):
        s = self.dna1
        t = self.dna2
        n, m = self.lengths()
        D = [[0 for i in xrange(n)] for j in xrange(m)]
        gap_score = -1
        for j in range(n):
            D[0][j] = gap_score*j
        for i in range(m):
            D[i][0] = gap_score*i
    
        for i in range(1,m):
            for j in range(1,n):
                match = D[i-1][j-1] + self.weightofs(s[j-1],t[i-1])
                gaps = D[i][j-1] + gap_score
                gapt = D[i-1][j] + gap_score
                D[i][j] = max(match,gaps,gapt)

        i = m-1
        j = n-1

        t_opt = ''
        s_opt = ''

        while i > 0 and j > 0:
            if D[i][j] - self.weightofs(s[j-1],t[i-1]) == D[i-1][j-1]:
                t_opt = t[i-1] + t_opt  
                s_opt = s[j-1] + s_opt
                i -= 1
                j -= 1
            elif D[i][j] - gap_score == D[i][j-1]:
                s_opt = s[j-1] + s_opt
                t_opt = "_" + t_opt
                j -= 1
            elif D[i][j] - gap_score == D[i-1][j]:
                s_opt = "_" + s_opt
                t_opt = t[i-1] + t_opt
                i -= 1
            else: print "ERROR"

        if j > 0:
            while j > 0:
                s_opt = s[j-1] + s_opt
                t_opt = "_" + t_opt 
                j -= 1
        elif i > 0:
            while i > 0:
                s_opt = "_" + s_opt
                t_opt = t[i-1] + t_opt 
                i -= 1
        return t_opt, s_opt

    def dna_output(self):
        t_opt, s_opt = self.dna_alignment()
        print t_opt
        print s_opt    

    def lengths(self):
        return len(self.dna1), len(self.dna2)

class profile:
    def __init__(list1=[], list2=[], length1=0, length2=0, power=0):
        if list1 != []:
            for elem in list1:
                try:
                    assert len(elem) == 5
                except:
                    AssertionError, elem
            self.prof1 = list1
        if list2 != []:
            for elem in list1:
                try:
                    assert len(elem) == 5
                except:
                    AssertionError, elem
            self.prof2 = list2
            
        if length1 != 0 and list1 == [] and power != 0:
            tmpProf = []
            for i in range(len(length1)):
                tmpArr = []
                summa = 0
                tmpPower = power
                seed = power // (4)
                for j in range(4):
                    k = random.randint(seed // 2,seed)
                    tmpArr.append(k)
                    summa += k
                    tmpPower -= k
                tmpArr.append(power - summa)
                tmpProf.append(tmpArr)
            self.prof1 = tmpProf
                
        if length2 != 0 and list2 == [] and power != 0:
            tmpProf = []
            for i in range(len(length1)):
                tmpArr = []
                summa = 0
                tmpPower = power
                seed = power // (4)
                for j in range(4):
                    k = random.randint(seed // 2,seed)
                    tmpArr.append(k)
                    summa += k
                    tmpPower -= k
                tmpArr.append(power - summa)
                tmpProf.append(tmpArr)
            self.prof2 = tmpProf

    def prof_alignment():
        pass

    def lengths():
        return len(self.list1)
        return len(self.list2)

    def prof_output():
        pass


class source_proteins(proteins):
    def get_alignment(self):
        self.prot_output()

class source_dna(dna):
    def get_alignment(self):
        self.dna_output()

class adapter:
    def __init__(self, source):
        self.source = source
    def get_alignment(self):
        self.source.get_alignment()

dna_sequences = source_dna("", "", 600, 590)
prot_sequences = source_proteins("", "", 150, 140)

list_of_data = [adapter(dna_sequences), adapter(prot_sequences)]
for elem in list_of_data:
    print elem.source.__class__.__name__
    elem.get_alignment()

    

